package com.dxy.hms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.dxy.hms.auth.LoginUser;
import com.dxy.hms.base.AppConstant;
import com.dxy.hms.base.UnifiedException;
import com.dxy.hms.model.entity.Menu;
import com.dxy.hms.model.entity.RoleMenu;
import com.dxy.hms.mapper.MenuMapper;
import com.dxy.hms.mapper.RoleMenuMapper;
import com.dxy.hms.model.dto.RoleMenuDto;
import com.dxy.hms.model.excel.MenuExcel;
import com.dxy.hms.model.vo.TreeVo;
import com.dxy.hms.service.MenuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dxy.hms.utils.LoginUtil;
import com.dxy.hms.utils.TreeUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.transaction.annotation.Transactional;

/**
 * <p>
 * 菜单表 服务实现类
 * </p>
 *
 * @author dxy
 * @since 2024-09-24
 */
@Slf4j
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {


    @Autowired
    private RoleMenuMapper roleMenuMapper;

    // 平台跟菜单编码
    @Value("${platform.root-menu-id:}")
    private String rootMenuId;

    @Override
    public void add(Menu entity) {
        Assert.notBlank(entity.getName(), "菜单名称不能为空");
        // 填充菜单编码和排序号（考虑到没有高并发，故不加同步锁，另外有主键约束也不会成功插入）
        fillMenuIdAndSort(entity);
        baseMapper.insert(entity);
    }


    @Override
    public void updById(Menu entity) {
        Assert.notBlank(entity.getId(), "菜单编码不能为空");
        baseMapper.updateById(entity);
    }

    @Override
    @Transactional
    public void delByIds(List<String> ids) {
        // 删除菜单（使用自定义删除，在菜单编码前拼接时间戳，防止下次新增时主键冲突）
        ids.forEach(id -> baseMapper.delById(id));
        // 删除其下的子菜单
        ids.forEach(id -> baseMapper.delChildMenu(id));
        // 删除和角色的关联关系
        ids.forEach(id -> roleMenuMapper.delByMenuId(id));
    }

    @Override
    public Menu getDetail(String id) {
        Menu menu = baseMapper.selectById(id);
        if (menu != null) {
            Menu parent = baseMapper.selectById(menu.getParentId());
            menu.setParentName(parent != null ? parent.getName() : "");
        }
        return menu;
    }

    @Override
    public IPage<Menu> getPage(Long pageNum, Long pageSize) {
        if (pageNum == null || pageNum <= 0) {
            pageNum = 1L;
        }
        if (pageSize == null || pageSize <= 0) {
            pageSize = 10L;
        }
        IPage<Menu> page = new Page<>(pageNum, pageSize);
        Wrapper<Menu> wrapper = new LambdaQueryWrapper<Menu>();
        return baseMapper.selectPage(page, wrapper);
    }

    @Override
    public List<TreeVo> getLoginUserMenu() {
        // 1. 获取用户拥有的状态为“启用”的菜单列表
        LoginUser loginUser = LoginUtil.getLoginUserOrException();
        LambdaQueryWrapper<Menu> wrapper = new LambdaQueryWrapper<Menu>()
                .likeRight(StrUtil.isNotEmpty(rootMenuId), Menu::getId, rootMenuId)
                .ne(StrUtil.isNotEmpty(rootMenuId), Menu::getId, rootMenuId);
        List<Menu> allMenus = baseMapper.selectList(wrapper);
        Set<String> authMenuIdSet = null;
        if (loginUser.isSuperAdmin()) { // 超级管理员不过滤权限
            authMenuIdSet = allMenus.stream()
                    .filter(m -> AppConstant.MENU_ENABLE_STATUS == m.getStatus())
                    .map(Menu::getId)
                    .collect(Collectors.toSet());
        } else { // 非超管根据userId查询出来的菜单编码进行过滤
            Set<String> ownedMenuIds = roleMenuMapper.getMenuIdByUserId(loginUser.getUserId());
            authMenuIdSet = allMenus.stream()
                    .filter(m -> AppConstant.MENU_ENABLE_STATUS == m.getStatus())
                    .filter(m -> ownedMenuIds.contains(m.getId()))
                    .map(Menu::getId)
                    .collect(Collectors.toSet());
        }

        TreeUtil<Menu> treeUtil = new TreeUtil<>(allMenus, Menu::toTreeVo);
        treeUtil.buildTree();
        return treeUtil.getFilteredTree(authMenuIdSet);
    }


    @Override
    public List<Menu> getList(String parentId, String name) {
        LambdaQueryWrapper<Menu> wrapper = new LambdaQueryWrapper<Menu>()
                .eq(StrUtil.isNotEmpty(parentId), Menu::getParentId, parentId)
                .like(StrUtil.isNotEmpty(name), Menu::getName, name)
                .orderByDesc(Menu::getCreateTime);
        return baseMapper.selectList(wrapper);
    }

    @Override
    public List<TreeVo> getTree(String nameKeyword) {
        // 此获取菜单树的方式是用在“菜单管理”页面，一般是超级管理员进行菜单管理，一般不需要设置数据权限
        List<Menu> allMenus = baseMapper.selectList(null);
        TreeUtil<Menu> treeUtil = new TreeUtil<>(allMenus, Menu::toTreeVo);
        if (StrUtil.isEmpty(nameKeyword)){// name为空则不进行模糊匹配，直接返回所有菜单
            return treeUtil.buildTree();
        } else {
            // 过滤name列中包含有nameKeyword关键词的菜单
            Set<String> visibleMenuIds = allMenus.stream()
                    .filter(m -> m.getName().contains(nameKeyword))
                    .map(Menu::getId)
                    .collect(Collectors.toSet());
            treeUtil.buildTree();
            return treeUtil.getFilteredTree(visibleMenuIds);
        }
    }

    @Override
    @Transactional
    public void addRoleMenu(RoleMenuDto dto) {
        Long roleId = dto.getRoleId();
        List<String> menuIdList = dto.getMenuIdList();
        Assert.notNull(roleId, "角色Id不能为空");
        if (CollUtil.isEmpty(menuIdList)){
            roleMenuMapper.delByRoleId(roleId);
        }else {
            List<RoleMenu> roleMenuList = menuIdList.stream().map(menuId -> new RoleMenu()
                    .setRoleId(roleId).setMenuId(menuId)).collect(Collectors.toList());
            roleMenuMapper.addOrIgnoreBatch(roleMenuList);
            // 删除角色Id下的，且没有在当前菜单编码列表内的关联关系
            roleMenuMapper.delByRoleIdAndNotInMenuIds(roleId, menuIdList);
        }
    }

    @Override
    public List<RoleMenu> getRoleMenu(Long roleId) {
        return roleMenuMapper.getByRoleId(roleId);
    }

    @Override
    @Transactional
    public void saveImportMenu(List<MenuExcel> list) {
        if (CollUtil.isEmpty(list)){
            return;
        }
        List<Menu> entityList = list.stream().map(item -> {
            String typeText = item.getTypeText();
            item.setType("菜单".equals(typeText) ? 1 : ("按钮".equals(typeText) ? 2 : null));
            return BeanUtil.toBean(item, Menu.class);
        }).collect(Collectors.toList());
        saveBatch(entityList);
    }


    private void fillMenuIdAndSort(Menu entity) {
        String id = null;
        int sort = 0;
        String parentId = entity.getParentId();
        if (StrUtil.isBlank(parentId)) { // 父级编码为空代表则设置为最顶级的菜单
            entity.setParentId(AppConstant.DEFAULT_PARENT_MENU_ID);
            String maxId = baseMapper.getMaxIdByParentId(entity.getParentId());
            sort = getSort(AppConstant.MENU_ID_PREFIX, maxId);
            id = AppConstant.MENU_ID_PREFIX + String.format("%02d", sort);
        } else {
            Menu parent = baseMapper.selectById(parentId);
            Assert.notNull(parent, "父菜单{}不存在", parentId);
            String maxId = baseMapper.getMaxIdByParentId(parentId);
            sort = getSort(parentId, maxId);
            id = parentId + String.format("%02d", sort);
        }
        entity.setId(id);
        if (entity.getSort() == null) {
            entity.setSort(sort);
        }
    }

    private int getSort(String prefix, String maxId) {
        if (StrUtil.isEmpty(maxId)) { // 表示还没有子菜单
            return 1;
        } else {
            int pre = Integer.parseInt(maxId.replace(prefix, ""));
            int next = pre + 1;
            if (next > 99) {
                throw new UnifiedException(500, "同一父菜单下的子菜单数不能超过99个，请合理划分菜单");
            }
            return next;
        }
    }


}
